/*
 * InvulgoTracker
 * Copyright (C) Thomas Andersen 2011 <thomas.1.andersen@gmail.com>
 * 
 * InvulgoTracker is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * InvulgoTracker is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <glade/glade.h>

#include "config.h"

#include "gettextmacro.h"


#include "time-view.h"

#include "task-view.h"
#include "task-time.h"
#include "day.h"
#include "prefs.h"
#include "util.h"
#include "recenttasks.h"
#include "invulgotrackerapp.h"

#define MY_ENCODING "ISO-8859-1"


extern "C" gboolean on_dialogDateWarning_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
    gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_CANCEL);
    return TRUE;
}


TimeView::TimeView()
{
    currentDlg = NULL;
}

TimeView::~TimeView()
{
    g_object_unref (this->model);
}

GtkTreeModel *TimeView::CreateModel()
{
    GtkListStore  *store;

    store = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_LONG, G_TYPE_STRING, G_TYPE_STRING);

    return GTK_TREE_MODEL (store);
}


extern "C" void cell_edited_callback (GtkCellRendererText *cell, gchar *path_string,gchar *new_text,gpointer user_data)
{
    InvulgoTrackerApp::app->TimeEdit(path_string, new_text);
}


extern "C" gint TimeView::sort_iter_compare_func (GtkTreeModel *model, GtkTreeIter  *a, GtkTreeIter  *b, gpointer userdata)
{
    gint sortcol = GPOINTER_TO_INT(userdata);
    gint ret = 0;

    gchar *name1, *name2;

    gtk_tree_model_get(model, a, sortcol, &name1, -1);
    gtk_tree_model_get(model, b, sortcol, &name2, -1);

    /* Sort by content. */
    switch (sortcol)
    {
    case COL_TIME:
        {
            if (name1 == NULL || name2 == NULL)
            {
                if (name1 == NULL && name2 == NULL)
                    break; /* both equal => ret = 0 */

                ret = (name1 == NULL) ? -1 : 1;
            }
            else
            {
                TaskTime t1(name1);
                TaskTime t2(name2);
                if (t1 < t2)
                    ret = -1;
                else if (t1 == t2)
                    ret = 0;
                else
                    ret = 1;
            }

            g_free(name1);
            g_free(name2);
        }
        break;
    default:
        g_return_val_if_reached(0);
    }

    return ret;
}


void TimeView::Init(GtkWidget *view)
{
    GtkCellRenderer *renderer;

    this->view = GTK_TREE_VIEW(view);

    this->model = CreateModel();

    /* Title */
    renderer = gtk_cell_renderer_text_new ();
    //g_object_set(renderer, "cell-background-set",TRUE, NULL);
    GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(_("Title"),  renderer, "text", COL_TITLE, "cell-background", COL_COLOR, NULL);
    //    gtk_tree_view_column_set_resizable(column, TRUE);
    g_object_set(column, "sizing",GTK_TREE_VIEW_COLUMN_AUTOSIZE,NULL);
    gtk_tree_view_insert_column(GTK_TREE_VIEW (view), column, -1);

    /* Time */
    renderer = gtk_cell_renderer_text_new ();
    g_object_set(renderer, "editable",TRUE, NULL);
    //g_object_set(renderer, "cell-background-set",TRUE, NULL);
    g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, this->model);
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1,  _("Time"),  renderer, "text", COL_TIME, "cell-background", COL_COLOR, NULL);

    /* Progress */
    renderer = gtk_cell_renderer_progress_new();
    //g_object_set(renderer, "cell-background-set",TRUE, NULL);
    column = gtk_tree_view_column_new_with_attributes(_("Used"),  renderer, "value", COL_PCT, "cell-background", COL_COLOR, NULL);
    gtk_tree_view_insert_column(GTK_TREE_VIEW (view), column, -1);

    Load();
    
    GtkTreeSortable *sortable = GTK_TREE_SORTABLE(model);
    gtk_tree_sortable_set_sort_func(sortable, COL_TIME, sort_iter_compare_func,GINT_TO_POINTER(COL_TIME), NULL);
    /* set initial sort order */
    gtk_tree_sortable_set_sort_column_id(sortable, COL_TIME, GTK_SORT_ASCENDING);

    gtk_tree_view_set_model (GTK_TREE_VIEW (view), this->model);

}


void TimeView::Edit(gchar *pathStr, gchar  *newStr)
{
    GtkTreeIter iter;
    TaskTime tt;

    if (strlen(newStr) > 5) {
        return;
    }
    tt.TimeFromString(newStr);
    
    gtk_tree_model_get_iter_from_string(this->model, &iter, pathStr);
    gchar *timeStr = tt.TimeText();
    gtk_list_store_set (GTK_LIST_STORE(this->model), &iter, COL_TIME, timeStr, -1);
    free(timeStr);
}


extern GladeXML *gxml;

bool TimeView::Warn()
{
    TaskTime tt;
    Prefs prefs;

    if (prefs.GetWarnOnDate()) {
        if (tt.GetYear() != year || tt.GetMonth() != month || tt.GetDay() != day) {
            currentDlg = glade_xml_get_widget (InvulgoTrackerApp::gxml, "dialogDateWarning");
            gint result = gtk_dialog_run (GTK_DIALOG (currentDlg));
            gtk_widget_hide(currentDlg);
            currentDlg = NULL;
            if (result != GTK_RESPONSE_OK) {
                return FALSE;
            }
        }
    }
    return TRUE;
}

bool TimeView::WarnDelete()
{
    Prefs prefs;

    if (prefs.GetWarnOnTimeDelete()) {
        currentDlg = glade_xml_get_widget (InvulgoTrackerApp::gxml, "dialogTimeDeleteWarning");
        gint result = gtk_dialog_run (GTK_DIALOG (currentDlg));
        gtk_widget_hide(currentDlg);
        currentDlg = NULL;
        if (result != GTK_RESPONSE_OK) {
            return FALSE;
        }
    }
    return TRUE;
}

void TimeView::Add(TaskId taskId)
{
    gchar *taskName;

    if (Warn())
        if (InvulgoTrackerApp::app->GetTaskView().Get(taskId, &taskName))
            AppendTask(taskName, taskId);
}

void TimeView::Add()
{
    gchar *taskName;
    TaskId taskId;

    if (Warn())
        if (InvulgoTrackerApp::app->GetTaskView().GetSelected(&taskName, taskId))
            AppendTask(taskName, taskId);
}

void TimeView::Add(GtkTreePath *path)
{
    gchar *taskName;
    TaskId taskId;

    if (Warn())
        if (InvulgoTrackerApp::app->GetTaskView().Get(path, &taskName, taskId))
            AppendTask(taskName, taskId);
}

void TimeView::AddNoCheck(TaskId taskId, TaskTime tt)
{
    gchar *taskName;
    if (InvulgoTrackerApp::app->GetTaskView().Get(taskId, &taskName)) {
        AppendTask(taskName, taskId, tt);
    }
}

void TimeView::AppendTask(gchar *name, TaskId taskId, TaskTime tt)
{
    GtkTreeIter iter;
    char *timeText = tt.TimeText();
    gtk_list_store_append (GTK_LIST_STORE(this->model), &iter);
    gtk_list_store_set (GTK_LIST_STORE(this->model), &iter,
                        COL_TITLE, name,
                        COL_TIME, timeText,
                        COL_ID, (const gchar *)taskId,
                        -1);
    g_free(name);
    free(timeText);
}

void TimeView::AppendTask(gchar *name, TaskId taskId)
{
    TaskTime tt;
    RecentTasks rt;
    rt.Add(taskId);
    rt.Save();

    AppendTask(name, taskId, tt);
}

void TimeView::Insert()
{
    gchar *taskName;
    TaskId taskId;
    GtkTreeIter iter;

    if (!gtk_tree_model_get_iter_first(this->model, &iter)) {
        Add();
        return;
    }
    
    if (Warn()) 
        if (InvulgoTrackerApp::app->GetTaskView().GetSelected(&taskName, taskId))
        {
            TaskTime tt;
            GtkTreeIter iter;
            GtkTreeIter sibling;
            GtkTreeSelection *selection;
            
            selection = gtk_tree_view_get_selection(this->view);
            if (gtk_tree_selection_get_selected(selection, NULL, &sibling))
            {
                RecentTasks rt;
                rt.Add(taskId);
                rt.Save();
                char *timeText = tt.TimeText();
                gtk_list_store_insert_before (GTK_LIST_STORE(this->model), &iter, &sibling);
                gtk_list_store_set (GTK_LIST_STORE(this->model), &iter,
                                    COL_TITLE, taskName,
                                    COL_TIME, timeText,
                                    COL_ID, (const gchar *)taskId,
                                    -1);
                free(timeText);
            }
        }
}

void TimeView::Delete()
{
    GtkTreeSelection *selection;
    GtkTreeIter       iter, newIter;
    bool selectionOk = FALSE;

    if (WarnDelete()) {
        selection = gtk_tree_view_get_selection(this->view);
        if (gtk_tree_selection_get_selected(selection, NULL, &iter))
        {
            newIter = iter;
            selectionOk = TRUE;
            if (!gtk_tree_model_iter_next(this->model, &newIter)) {
                newIter = iter;
                GtkTreePath *path = gtk_tree_model_get_path(model, &newIter);
                if (gtk_tree_path_prev(path)) {
                    gtk_tree_model_get_iter(model, &newIter, path);
                    gtk_tree_selection_select_iter(selection, &newIter);
                }
                else
                    selectionOk = FALSE;
                gtk_tree_path_free(path);
            }
            gtk_list_store_remove(GTK_LIST_STORE(this->model), &iter);
            if (selectionOk)
                gtk_tree_selection_select_iter(selection, &newIter);
            else
                gtk_tree_selection_unselect_all(selection);
        }
    }
}


bool TimeView::Find(TaskId taskId)
{
    bool found = false;

    GtkTreeIter iter;
    if (gtk_tree_model_get_iter_first(this->model, &iter)) {
        gchar *value;

        do {
            gtk_tree_model_get(this->model, &iter,
                               COL_ID, &value,
                               -1);
            TaskId ti(value);
            g_free(value);
            if (ti == taskId)
                found = true;
        } while(gtk_tree_model_iter_next(this->model, &iter));
    }
    return found;
}

void TimeView::Save()
{
    Day day(this->year, this->month, this->day);

    day.SaveInit();
    
    GtkTreeIter iter;
    if (gtk_tree_model_get_iter_first(this->model, &iter)) {
        gchar *name;
        gchar *timeStr;
        gchar *value;

        do {
            gtk_tree_model_get(this->model, &iter,
                               COL_TITLE, &name,
                               COL_TIME, &timeStr,
                               COL_ID, &value,
                               -1);
            TaskId ti(value);
            g_free(value);
            day.SaveData(name, timeStr, ti);
        } while(gtk_tree_model_iter_next(this->model, &iter));
    }

    day.SaveFinal();
}


void TimeView::Load()
{
    Day day(this->year, this->month, this->day);

    gtk_list_store_clear(GTK_LIST_STORE(this->model));
        
    day.Load(this);
}


void TimeView::Data(gchar *name, gchar *timeStr, TaskId id)
{
    GtkTreeIter iter;

    gtk_list_store_append (GTK_LIST_STORE(this->model), &iter);
    gtk_list_store_set (GTK_LIST_STORE(this->model), &iter,
                        COL_TITLE, name,
                        COL_TIME, timeStr,
                        COL_ID, (const gchar *)id,
                        -1);
    g_free(name);
    free(timeStr);
}

void TimeView::SetPct(TaskId &id, glong pct)
{
    GtkTreeIter iter;

    if (gtk_tree_model_get_iter_first(this->model, &iter)) {
        gchar *value;

        do {
            gtk_tree_model_get(this->model, &iter, COL_ID, &value,-1);
            TaskId ti(value);
            g_free(value);
            if (ti == id) {
                gtk_list_store_set (GTK_LIST_STORE(this->model), &iter,
                                    COL_PCT, pct,
                                    -1);
            }
        } while(gtk_tree_model_iter_next(this->model, &iter));
    }
}


void TimeView::SetColor(TaskId &id, const gchar *color)
{
    GtkTreeIter iter;

    if (gtk_tree_model_get_iter_first(this->model, &iter)) {
        gchar *value;

        do {
            gtk_tree_model_get(this->model, &iter, COL_ID, &value,-1);
            TaskId ti(value);
            g_free(value);
            if (ti == id) {
                gtk_list_store_set (GTK_LIST_STORE(this->model), &iter,
                                    COL_COLOR, color,
                                    -1);
            }
        } while(gtk_tree_model_iter_next(this->model, &iter));
    }
}


